
#include "Node.h"
#include "Symbols.h"
#include "util.h"
#include "rutil/ResipAssert.h"

using namespace resip;
using namespace codegen;

#define RESIPROCATE_SUBSYSTEM LogSystem::LOGMODULE


Node::~Node()
{
    NodeContainer::const_iterator it = childs_.begin();
    while (it != childs_.end())
    {
        delete *it;
        ++it;
    }
    childs_.clear();
}

resip::Data Node::str_type(bool use_alias/* = false */) const
{
    switch (type_)
    {
    case Node::VT_Int:
        return "int";
    case Node::VT_UInt:
        return "unsigned int";
    case Node::VT_Int64:
        return "int64_t";
    case Node::VT_UInt64:
        return "uint64_t";
    case Node::VT_Float:
        return "float";
    case Node::VT_Double:
        return "double";
    case Node::VT_Bool:
        return "bool";
    case Node::VT_String:
        return "std::string";
    case Node::VT_Object:
    {
        resip::Data type(key_);
        type += "_t";
        //type[0] = std::toupper(type[0]);
        return type;
    }
    case Node::VT_Array:
    {
        resip_assert(!childs_.empty());

        if (use_alias)
        {
            return key_ + "Container";
        }
        else
        {
            resip::Data typestr("std::vector<");
            const Node* child = childs_.front();
            typestr += child->str_type();
            if (child->type_ == VT_Object)
            {
                typestr += "*";
            }
            typestr += ">";
            return typestr;
        }
    }
    default:
        resip_assert(false);
        return "";
    }
}

resip::Data Node::str_name() const
{
    if (isArray())
    {
        return key_ + "s_";
    }
    return (key_ + "_");
}

resip::Data Node::str_arr_size_name() const
{
    resip::Data name("n_");
    name += key_;
    name += "_count";
    return name;
}

resip::Data Node::str_arr_size_index_name() const
{
    resip::Data name("n_");
    name += key_;
    name += "_i";
    return name;
}

resip::Data Node::str_com_tmp_name() const
{
    resip::Data name("__");
    name += key_;
    return name;
}

resip::Data Node::str_parent_class_name() const
{
    return (parent_
        ?
        (parent_->isObject()
            ? parent_->str_type()
            : (parent_->isArray()
                ? parent_->str_parent_class_name()
                : ""))
        : ""
        );
}

EncodeStream& Node::encode(EncodeStream& ostm) const
{
    if (isObject())
    {
        /// class declaration begin
        encode_class_begin(ostm); // class xxx {
        /// child objects define
        ostm << LeadingIndent(depth_) << "public:" << Symbols::LF
            << LeadingIndent(depth_ + 1) << "/// child objects define" << Symbols::LF;
        for (auto node : childs_)
        {
            if (node->isCompound())
            {
                node->encode(ostm);
            }
        }

        /// object ctor and dector
        ostm << LeadingIndent(depth_ + 1) << "/// object ctor and dector functions" << Symbols::LF;
        encode_class_ctor_dector(ostm);

        /// member get-set functions
        ostm << LeadingIndent(depth_ + 1) << "/// member get-set functions" << Symbols::LF;
        for (auto node : childs_)
        {
            node->encode_member_get_set_function(ostm);
        }

        /// custom member functions
        //ostm << LeadingIndent(depth_, indent_ctrl_) << "protected:" << Symbols::LF;
        //ostm << LeadingIndent(depth_ + 1, indent_ctrl_) << "/// custom member functions" << Symbols::LF;
        encode_custom_function(ostm);

        /// member declaration
        ostm << LeadingIndent(depth_) << "protected:" << Symbols::LF
            << LeadingIndent(depth_ + 1) << "/// member declaration" << Symbols::LF;
        for (auto node : childs_)
        {
            node->encode_member_declaration(ostm);
        }

        encode_class_end(ostm); // };
    }
    else if (isArray())
    {
        for (auto node : childs_)
        {
            if (node->isObject())
            {
                node->encode(ostm);
            }
            /// typedef array type
            ostm << LeadingIndent(depth_)
                << "typedef " << str_type() << " " << str_type(true) << Symbols::SEMI_COLON << Symbols::LF << Symbols::LF;

            break;
        }
    }

    return ostm;
}

EncodeStream& Node::encode_class_begin(EncodeStream& ostm) const
{
    ostm << LeadingIndent(depth_) << "class " << str_type() << Symbols::LF
        << LeadingIndent(depth_) << "{" << Symbols::LF;
    return ostm;
}

EncodeStream& Node::encode_class_end(EncodeStream& ostm) const
{
    ostm << LeadingIndent(depth_) << "};" << Symbols::LF;
    return ostm;
}

EncodeStream& Node::encode_class_ctor_dector(EncodeStream& ostm) const
{
    encode_class_ctor(ostm);
    encode_class_dector(ostm);
    return ostm;
}

EncodeStream& Node::encode_class_ctor(EncodeStream& ostm) const
{
    ostm << LeadingIndent(depth_ + 1) << str_type() << "()" << Symbols::LF
        << LeadingIndent(depth_ + 1) << "{" << Symbols::LF;
    for (auto node : childs_)
    {
        node->encode_member_init(ostm);
    }
    ostm << LeadingIndent(depth_ + 1) << "}" << Symbols::LF;
    return ostm;
}

EncodeStream& Node::encode_class_dector(EncodeStream& ostm) const
{
    ostm << LeadingIndent(depth_ + 1) << "~" << str_type() << "()" << Symbols::LF
        << LeadingIndent(depth_ + 1) << "{" << Symbols::LF;
    for (auto node : childs_)
    {
        node->encode_member_uninit(ostm);
    }
    ostm << LeadingIndent(depth_ + 1) << "}" << Symbols::LF;
    return ostm;
}

EncodeStream& Node::encode_member_init(EncodeStream& ostm) const
{
    if (!(isArray() || isObject()))
    {
        if (type_ == VT_String)
        {
            return ostm << LeadingIndent(depth_ + 1) << str_name() << " = \"" << val_ << "\";" << Symbols::LF;
        }
        else
        {
            return ostm << LeadingIndent(depth_ + 1) << str_name() << " = " << val_ << ";" << Symbols::LF;
        }
    }

    /*
    switch (type_)
    {
    case Node::VT_Int:
    case Node::VT_UInt:
    case Node::VT_Int64:
    case Node::VT_UInt64:
        return ostm << LeadingIndent(depth_ + 1) << str_name() << " = 0;" << Symbols::LF;
    case Node::VT_Float:
    case Node::VT_Double:
        return ostm << LeadingIndent(depth_ + 1) << str_name() << " = 0.0;" << Symbols::LF;
    case Node::VT_Bool:
        return ostm << LeadingIndent(depth_ + 1) << str_name() << " = false;" << Symbols::LF;
    }
    */

    return ostm;
}


EncodeStream& Node::encode_member_uninit(EncodeStream& ostm) const
{
    if (isArray())
    {
        if (!childs_.empty())
        {
            if ((*childs_.begin())->isObject())
            {
                ostm << LeadingIndent(depth_ + 1) << str_type(true) << "::const_iterator " << str_com_tmp_name() << " = " << str_name() << ".begin();" << Symbols::LF
                    << LeadingIndent(depth_ + 1) << "for (; "<< str_com_tmp_name() << " != " << str_name() << ".end(); ++" << str_com_tmp_name() << ")" << Symbols::LF
                    << LeadingIndent(depth_ + 1) << "{" << Symbols::LF
                    << LeadingIndent(depth_ + 2) << "delete *" << str_com_tmp_name() << ";" << Symbols::LF
                    << LeadingIndent(depth_ + 1) << "}" << Symbols::LF
                    << LeadingIndent(depth_ + 1) << str_name() << ".clear();" << Symbols::LF;
            }
            else if ((*childs_.begin())->isArray())
            {
                resip_assert(false);
            }
        }
    }
    return ostm;
}

EncodeStream& Node::encode_member_get_set_function(EncodeStream& ostm) const
{
    Data func_name(key_);
    if (isArray())
    {
        func_name += "s";
    }

    ostm << LeadingIndent(depth_) << "const " << str_type(true) << "& " << func_name << "() const { return " << str_name() << Symbols::SEMI_COLON << " }" << Symbols::LF;
    ostm << LeadingIndent(depth_) << str_type(true) << "& " << func_name << "() { return " << str_name() << Symbols::SEMI_COLON << " }" << Symbols::LF;

    return ostm;
}

EncodeStream& Node::encode_custom_function(EncodeStream& ostm) const
{
    return ostm;
}

EncodeStream& Node::encode_member_declaration(EncodeStream& ostm) const
{
    return ostm << LeadingIndent(depth_) << str_type(true) << Symbols::SPACE << str_name() << Symbols::SEMI_COLON << Symbols::LF;
}


